home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / devel / tcl / tclx7_31.z / tclx7_31 / tcldev / tclX7.3a-p1 / tclsrc / help.tcl < prev    next >
Encoding:
Text File  |  1994-01-11  |  10.4 KB  |  338 lines

  1. #
  2. # help.tcl --
  3. #
  4. # Tcl help command. (see TclX manual)
  5. #------------------------------------------------------------------------------
  6. # Copyright 1992-1993 Karl Lehenbauer and Mark Diekhans.
  7. #
  8. # Permission to use, copy, modify, and distribute this software and its
  9. # documentation for any purpose and without fee is hereby granted, provided
  10. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  11. # Mark Diekhans make no representations about the suitability of this
  12. # software for any purpose.  It is provided "as is" without express or
  13. # implied warranty.
  14. #------------------------------------------------------------------------------
  15. # The help facility is based on a hierarchical tree of subjects (directories)
  16. # and help pages (files).  There is a virtual root to this tree. The root
  17. # being the merger of all "help" directories found along the $auto_path
  18. # variable.
  19. #------------------------------------------------------------------------------
  20. # $Id: help.tcl,v 3.1 1994/01/11 05:18:48 markd Exp $
  21. #------------------------------------------------------------------------------
  22. #
  23.  
  24. #@package: TclX-help help helpcd helppwd apropos
  25.  
  26. #------------------------------------------------------------------------------
  27. # Return a list of help root directories.
  28.  
  29. proc help:RootDirs {} {
  30.     global auto_path
  31.     set roots {}
  32.     foreach dir $auto_path {
  33.         if [file isdirectory $dir/help] {
  34.             lappend roots $dir/help
  35.         }
  36.     }
  37.     return $roots
  38. }
  39.  
  40. #------------------------------------------------------------------------------
  41. # Take a path name which might have "." and ".." elements and flatten them out.
  42. # Also removes trailing and adjacent "/", unless its the only character.
  43.  
  44. proc help:FlattenPath pathName {
  45.     set newPath {}
  46.     foreach element [split $pathName /] {
  47.         if {"$element" == "." || [lempty $element]} continue
  48.  
  49.         if {"$element" == ".."} {
  50.             if {[llength [join $newPath /]] == 0} {
  51.                 error "Help: name goes above subject directory root" {} \
  52.                     [list TCLXHELP NAMEABOVEROOT $pathName]
  53.             }
  54.             lvarpop newPath [expr [llength $newPath]-1]
  55.             continue
  56.         }
  57.         lappend newPath $element
  58.     }
  59.     set newPath [join $newPath /]
  60.  
  61.     # Take care of the case where we started with something line "/" or "/."
  62.  
  63.     if {("$newPath" == "") && [string match "/*" $pathName]} {
  64.         set newPath "/"
  65.     }
  66.         
  67.     return $newPath
  68. }
  69.  
  70. #------------------------------------------------------------------------------
  71. # Given a pathName relative to the virtual help root, convert it to a list of
  72. # real file paths.  A list is returned because the path could be "/", returning
  73. # a list of all roots. The list is returned in the same order of the auto_path
  74. # variable. If path does not start with a "/", it is take as relative to the
  75. # current help subject.  Note:  The root directory part of the name is not
  76. # flattened.  This lets other commands pick out the part relative to the
  77. # one of the root directories.
  78.  
  79. proc help:ConvertPath pathName {
  80.     global TCLXENV
  81.  
  82.     if {![string match "/*" $pathName]} {
  83.         if {"$TCLXENV(help:curSubject)" == "/"} {
  84.             set pathName "/$pathName"
  85.         } else {
  86.             set pathName "$TCLXENV(help:curSubject)/$pathName"
  87.         }
  88.     }
  89.     set pathName [help:FlattenPath $pathName]
  90.  
  91.     # If the virtual root is specified, return a list of directories.
  92.  
  93.     if {$pathName == "/"} {
  94.         return [help:RootDirs]
  95.     }
  96.  
  97.     # Not the virtual root find the first match.
  98.  
  99.     foreach dir [help:RootDirs] {
  100.         if [file readable $dir/$pathName] {
  101.             return [list $dir/$pathName]
  102.         }
  103.     }
  104.     error "\"$pathName\" does not exist" {} \
  105.         [list TCLXHELP NOEXIST $pathName]
  106. }
  107.  
  108. #------------------------------------------------------------------------------
  109. # Return the virtual root relative name of the file given its absolute path.
  110. # The root part of the path should not have been flattened, as we would not
  111. # be able to match it.
  112.  
  113. proc help:RelativePath pathName {
  114.     foreach dir [help:RootDirs] {
  115.         if {[csubstr $pathName 0 [clength $dir]] == $dir} {
  116.             set name [csubstr $pathName [clength $dir] end]
  117.             if {$name == ""} {set name /}
  118.             return $name
  119.         }
  120.     }
  121.     if ![info exists found] {
  122.         error "problem translating \"$pathName\"" {} [list TCLXHELP INTERROR]
  123.     }
  124. }
  125.  
  126. #------------------------------------------------------------------------------
  127. # Given a list of path names to subjects generated by ConvertPath, return
  128. # the contents of the subjects.  Two lists are returned, subjects under that
  129. # subject and a list of pages under the subject.  Both lists are returned
  130. # sorted.  This merges all the roots into a virtual root.  pathName is the
  131. # string that was passed to ConvertPath and is used for error reporting.
  132. # *.brk files are not returned.
  133.  
  134. proc help:ListSubject {pathName pathList subjectsVar pagesVar} {
  135.     upvar $subjectsVar subjects $pagesVar pages
  136.  
  137.     set subjects {}
  138.     set pages {}
  139.     set foundDir 0
  140.     foreach dir $pathList {
  141.         if ![file isdirectory $dir] continue
  142.         set foundDir 1
  143.         foreach file [glob -nocomplain $dir/*] {
  144.             if [string match *.brf $file] continue
  145.             if [file isdirectory $file] {
  146.                 lappend subjects [file tail $file]/
  147.             } else {
  148.                 lappend pages [file tail $file]
  149.             }
  150.         }
  151.     }
  152.     if !$foundDir {
  153.         if [cequal $pathName /] {
  154.             global auto_path
  155.             error "no \"help\" directories found on auto_path ($auto_path)" {} \
  156.                 [list TCLXHELP NOHELPDIRS]
  157.         } else {
  158.             error "\"$pathName\" is not a subject" {} \
  159.                 [list TCLXHELP NOTSUBJECT $pathName]
  160.         }
  161.     }
  162.     set subjects [lsort $subjects]
  163.     set pages [lsort $pages]
  164.     return {}
  165. }
  166.  
  167. #------------------------------------------------------------------------------
  168. # Display a line of output, pausing waiting for input before displaying if the
  169. # screen size has been reached.  Return 1 if output is to continue, return
  170. # 0 if no more should be outputed, indicated by input other than return.
  171. #
  172.  
  173. proc help:Display line {
  174.     global TCLXENV
  175.     if {$TCLXENV(help:lineCnt) >= 23} {
  176.         set TCLXENV(help:lineCnt) 0
  177.         puts stdout ":" nonewline
  178.         flush stdout
  179.         gets stdin response
  180.         if {![lempty $response]} {
  181.             return 0}
  182.     }
  183.     puts stdout $line
  184.     incr TCLXENV(help:lineCnt)
  185. }
  186.  
  187. #------------------------------------------------------------------------------
  188. # Display a help page (file).
  189.  
  190. proc help:DisplayPage filePath {
  191.  
  192.     set inFH [open $filePath r]
  193.     while {[gets $inFH fileBuf] >= 0} {
  194.         if {![help:Display $fileBuf]} {
  195.             break}
  196.     }
  197.     close $inFH
  198. }    
  199.  
  200. #------------------------------------------------------------------------------
  201. # Display a list of file names in a column format. This use columns of 14 
  202. # characters 3 blanks.
  203.  
  204. proc help:DisplayColumns {nameList} {
  205.     set count 0
  206.     set outLine ""
  207.     foreach name $nameList {
  208.         if {$count == 0} {
  209.             append outLine "   "}
  210.         append outLine $name
  211.         if {[incr count] < 4} {
  212.             set padLen [expr 17-[clength $name]]
  213.             if {$padLen < 3} {
  214.                set padLen 3}
  215.             append outLine [replicate " " $padLen]
  216.         } else {
  217.            if {![help:Display $outLine]} {
  218.                return}
  219.            set outLine ""
  220.            set count 0
  221.         }
  222.     }
  223.     if {$count != 0} {
  224.         help:Display [string trimright $outLine]}
  225.     return
  226. }
  227.  
  228. #------------------------------------------------------------------------------
  229. # Display help on help, the first occurance of a help page called "help" in
  230. # the help root.
  231.  
  232. proc help:HelpOnHelp {} {
  233.     set helpPage [lindex [help:ConvertPath /help] 0]
  234.     if [lempty $helpPage] {
  235.         error "No help page on help found" {} \
  236.             [list TCLXHELP NOHELPPAGE]
  237.     }
  238.     help:DisplayPage $helpPage
  239. }
  240.  
  241. #------------------------------------------------------------------------------
  242. # Help command.
  243.  
  244. proc help {{what {}}} {
  245.     global TCLXENV
  246.  
  247.     set TCLXENV(help:lineCnt) 0
  248.  
  249.     # Special case "help help", so we can get it at any level.
  250.  
  251.     if {($what == "help") || ($what == "?")} {
  252.         help:HelpOnHelp
  253.         return
  254.     }
  255.  
  256.     set pathList [help:ConvertPath $what]
  257.     if [file isfile [lindex $pathList 0]] {
  258.         help:DisplayPage [lindex $pathList 0]
  259.         return
  260.     }
  261.  
  262.     help:ListSubject $what $pathList subjects pages
  263.     set relativeDir [help:RelativePath [lindex $pathList 0]]
  264.  
  265.     if {[llength $subjects] != 0} {
  266.         help:Display "\nSubjects available in $relativeDir:"
  267.         help:DisplayColumns $subjects
  268.     }
  269.     if {[llength $pages] != 0} {
  270.         help:Display "\nHelp pages available in $relativeDir:"
  271.         help:DisplayColumns $pages
  272.     }
  273. }
  274.  
  275.  
  276. #------------------------------------------------------------------------------
  277. # helpcd command.  The name of the new current directory is assembled from the
  278. # current directory and the argument.
  279.  
  280. proc helpcd {{dir /}} {
  281.     global TCLXENV
  282.  
  283.     set pathName [lindex [help:ConvertPath $dir] 0]
  284.  
  285.     if {![file isdirectory $pathName]} {
  286.         error "\"$dir\" is not a subject" \
  287.             [list TCLXHELP NOTSUBJECT $dir]
  288.     }
  289.  
  290.     set TCLXENV(help:curSubject) [help:RelativePath $pathName]
  291.     return
  292. }
  293.  
  294. #------------------------------------------------------------------------------
  295. # Helpcd main.
  296.  
  297. proc helppwd {} {
  298.         global TCLXENV
  299.         echo "Current help subject: $TCLXENV(help:curSubject)"
  300. }
  301.  
  302. #------------------------------------------------------------------------------
  303. # apropos command.  This search the 
  304.  
  305. proc apropos {regexp} {
  306.     global TCLXENV
  307.  
  308.     set TCLXENV(help:lineCnt) 0
  309.  
  310.     set ch [scancontext create]
  311.     scanmatch -nocase $ch $regexp {
  312.         set path [lindex $matchInfo(line) 0]
  313.         set desc [lrange $matchInfo(line) 1 end]
  314.         if {![help:Display [format "%s - %s" $path $desc]]} {
  315.             set stop 1
  316.             return}
  317.     }
  318.     set stop 0
  319.     foreach dir [help:RootDirs] {
  320.         foreach brief [glob -nocomplain $dir/*.brf] {
  321.             set briefFH [open $brief]
  322.             scanfile $ch $briefFH
  323.             close $briefFH
  324.             if $stop break
  325.         }
  326.         if $stop break
  327.     }
  328.     scancontext delete $ch
  329. }
  330.  
  331. #------------------------------------------------------------------------------
  332. # One time initialization done when the file is sourced.
  333. #
  334. global TCLXENV
  335.  
  336. set TCLXENV(help:curSubject) "/"
  337.